package org.easymodelers.gmf.creator.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.easymodelers.gmf.utils.GmfModelsUtils;
import org.easymodelers.gmf.utils.ModelCreatorCallback;
import org.easymodelers.gmf.utils.ModelsManager;
import org.easymodelers.modeler.data.ElementDefinition;
import org.easymodelers.modeler.data.ModelerDefinition;
import org.easymodelers.modeler.handler.ElementDefinitionHandler;
import org.easymodelers.processing.ModelerUpdate;
import org.eclipse.emf.ecore.EObject;

public abstract class AbstractGenericGmfCreator<T extends EObject> extends AbstractPreModelsCreator {
	private Map<String,ElementDefinitionHandler> handlers = new HashMap<String, ElementDefinitionHandler>();

	public abstract void registerDefaultElementDefinitionHandlers();
	
	public void registerElementDefinitionHandler(String type, ElementDefinitionHandler handler) {
		handler.initializeHandler();
		handlers.put(type, handler);
	}
	
	protected ElementDefinitionHandler getElementDefinitionHandler(String type) {
		return handlers.get(type);
	}
	
	private void handleElementDefinition(ElementDefinition elementDefinition,
								ModelsManager modelsManager, List<ModelerUpdate> updates) {
		System.out.println("######################## Handle element type "+elementDefinition.getType());
		ElementDefinitionHandler handler = getElementDefinitionHandler(elementDefinition.getType());
		if (handler!=null) {
			List<ModelerUpdate> updatesForElement = handler.handleElementDefinition(elementDefinition, modelsManager, null);
			if (updatesForElement!=null) {
				updates.addAll(updatesForElement);
			}
		} else {
			System.out.println("No handler for type "+elementDefinition.getType());
		}

		Map<String,Set<ElementDefinition>> subElements = elementDefinition.getSubElements();
		if (subElements!=null) {
			for (String key : subElements.keySet()) {
				Set<ElementDefinition> subElementDefinitions = subElements.get(key);
				for (ElementDefinition subElementDefinition : subElementDefinitions) {
					handleElementDefinition(subElementDefinition, modelsManager, updates);
				}
			}
		}
	}

	//protected abstract T createRootEObject();
	protected abstract ModelsManager createModelsManager(String modelPath);
	protected abstract T getRootEObject(ModelsManager modelsManager);
	
	public List<ModelerUpdate> createGmfModel(String modelPath, ModelerDefinition modeler)	throws IOException {
		ModelsManager modelsManager = createModelsManager(modelPath);
		final T rootEObject = getRootEObject(modelsManager);
		
		List<ModelerUpdate> updates = new ArrayList<ModelerUpdate>();
		Set<ElementDefinition> elements = modeler.getElements();
		for (ElementDefinition elementDefinition : elements) {
			handleElementDefinition(elementDefinition, modelsManager, updates);
		}

		GmfModelsUtils.createGmfModel(modelPath, new ModelCreatorCallback<T>() {
			public T createModelRootElement() throws IOException {
				return rootEObject;
			}
		});
		
		return updates;
	}

}
